HDU 5803 Zhu’s Math Problem(数位dp)

题意:

$给定A,B,C,D\in[1, 10^{18} ]$
$求满足a+c>b+d \cup a+d\ge b+c且a\in[0, A], b\in [0, B], c\in[0, C], d\in[0, D]$
$的不同的四元组(a, b, c, d)个数$

分析:

$数位dp,每次转移4个数,开1个msk表示每个数被限制的情况$
$加状态表示a+c>b+d和a+d\ge b+c$
$f[i][msk][f1][f2]:=从高到低第i位,状态如上的满足四元组个数$
$分解成二进制来转移,仔细想想可以发现f1,f2\in[-2, 2]$
$如果越界一定满足/不满足,所以就可以做了$
$时间复杂度O(60\times 2^4\times 5\times 5\times 2^4)$

//
//  Created by TaoSama on 2016-08-08
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
LL a[4];
int f[61][1 << 4][5][5]; //[-2, 2]

inline void add(int& x, int y) {
    if((x += y) >= MOD) x -= MOD;
}

//a+c-b-d>0 a+d-b-c>=0
const int O = 2; //offset
int dfs(int x, int msk, int f1, int f2) {
    if(x == -1) return f1 > 0 && f2 >= 0;
    int& ret = f[x][msk][f1 + O][f2 + O];
    if(~ret) return ret;

    ret = 0;
    int to[4];
    for(int i = 0; i < 4; ++i) to[i] = (msk >> i & 1) ? 1 : a[i] >> x & 1;
    for(int a = 0; a <= to[0]; ++a) {
        for(int b = 0; b <= to[1]; ++b) {
            for(int c = 0; c <= to[2]; ++c) {
                for(int d = 0; d <= to[3]; ++d) {
                    int newF1 = f1 * 2 + a + c - b - d;
                    int newF2 = f2 * 2 + a + d - b - c;
                    if(newF1 < -2 || newF2 < -2) continue;
                    newF1 = min(newF1, 2);
                    newF2 = min(newF2, 2);

                    int newMsk = msk;
                    if(a != to[0]) newMsk |= 1 << 0;
                    if(b != to[1]) newMsk |= 1 << 1;
                    if(c != to[2]) newMsk |= 1 << 2;
                    if(d != to[3]) newMsk |= 1 << 3;

                    add(ret, dfs(x - 1, newMsk, newF1, newF2));
                }
            }
        }
    }
    return ret;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        for(int i = 0; i < 4; ++i) scanf("%I64d", a + i);
        memset(f, -1, sizeof f);
        printf("%d\n", dfs(60, 0, 0, 0));
    }

    return 0;
}